
import machine
import time
import math
import struct
from micropython import const

class MicroBlueError(Exception):
    pass


def my_uuid():
    uid = machine.unique_id()
    return ''.join(['{:02x}'.format(b) for b in uid])



class FollowerTimer:
    def __init__(self):
        self.callback = None

    def _callback(self, timer_obj):
        self.callback()

    def connect(self, callback, period):
        self.callback = callback
        tim = machine.Timer(0)
        tim.init(period=period, mode=machine.Timer.PERIODIC, callback=self._callback)


class FollowerLine8Ttl:

    def __init__(self, rx, tx):
        """ 这里注意是相反的 """
        if my_uuid() not in ['6825ddf0eb60', '6825ddf0fd3c', '6825ddf11278']:
            raise MicroBlueError("主板序列号未注册，请联系微蓝课堂")
        self.rx = tx
        self.tx = rx
        self.uart = machine.UART(1, tx=self.tx, rx=self.rx, baudrate=115200)

    @staticmethod
    def check_code(buf):
        sum_val = 0
        index = 2
        index_end = buf[3] + index + 2
        for i in range(index, index_end):
            sum_val += buf[i]
        check = (~sum_val) & 0xFF
        return check

    def read_data(self):
        self.uart.write(bytes([1]))
        loop_cnt = 0
        while True:
            loop_cnt += 1
            if loop_cnt > 100:
                return [0]*8
            self.uart.write(bytes([1]))
            while self.uart.any():
                time.sleep_ms(2)
                response = self.uart.read(1)
                if len(response) > 0:
                    data = [0] * 8
                    for length in range(8):
                        data[length] = (response[0] >> length) & 0x01
                    return data
                print("world")
                

class FollowerLine8I2c:
    _ANALOG_VALUES: int = const(6)
    _DIGITAL_VALUES: int = const(5)

    def __init__(self, sda=21, scl=22):
        if my_uuid() not in ['6825ddf0eb60', '6825ddf0fd3c', '6825ddf11278']:
            raise MicroBlueError("主板序列号未注册，请联系微蓝课堂")
        
        self._i2c = machine.I2C(0, sda=machine.Pin(sda), scl=machine.Pin(scl))
        self._i2c_address = 0x5D

    def analog_values(self):
        return struct.unpack("<HHHHHHHH", self._i2c.readfrom_mem(self._i2c_address, self._ANALOG_VALUES, 16))

    def digital_values(self):
        while True:
            try:
                resp = self._i2c.readfrom_mem(self._i2c_address, self._DIGITAL_VALUES, 1)[0]
                data = [0] * 8
                for length in range(8):
                    data[length] = (resp >> length) & 0x01
                return data
            except Exception as e:
                print(e)
                time.sleep_ms(50)


class FollowerLine8I2cYb:
    _DIGITAL_VALUES: int = const(0x30)

    def __init__(self, sda=21, scl=22):
        self._i2c = machine.I2C(0, sda=machine.Pin(sda), scl=machine.Pin(scl))
        self._i2c_address = 0x12

    def digital_values(self):
        resp = self._i2c.readfrom_mem(self._i2c_address, self._DIGITAL_VALUES, 1)[0]
        data = [0] * 8
        for length in range(8):
            data[length] = (resp >> length) & 0x01
        return data


class FollowerLine5Gray:
    _DEVICE_ID: int = const(0x00)
    _VERSION: int = const(0x01)
    _ANALOG_VALUES: int = const(0x10)
    _DIGITAL_VALUES: int = const(0x1A)
    _HIGH_THRESHOLDS: int = const(0x1C)
    _LOW_THRESHOLDS: int = const(0x26)

    def __init__(self, sda=21, scl=22):
        self._i2c = machine.I2C(0, sda=machine.Pin(sda), scl=machine.Pin(scl))
        self._i2c_address = 0x50

    @property
    def device_id(self):
        return self._i2c.readfrom_mem(self._i2c_address, self._DEVICE_ID, 1)[0]

    @property
    def version(self):
        return self._i2c.readfrom_mem(self._i2c_address, self._VERSION, 1)[0]

    @property
    def high_thresholds(self):
        return struct.unpack("<HHHHH", self._i2c.readfrom_mem(self._i2c_address, self._HIGH_THRESHOLDS, 10))

    @high_thresholds.setter
    def high_thresholds(self, values: tuple):
        self._i2c.writeto_mem(self._i2c_address, self._HIGH_THRESHOLDS, struct.pack("<HHHHH", *values))

    @property
    def low_thresholds(self):
        return struct.unpack("<HHHHH", self._i2c.readfrom_mem(self._i2c_address, self._LOW_THRESHOLDS, 10))

    @low_thresholds.setter
    def low_thresholds(self, values: tuple):
        self._i2c.writeto_mem(self._i2c_address, self._LOW_THRESHOLDS, struct.pack("<HHHHH", *values))

    def high_threshold(self, channel, value):
        self._i2c.writeto_mem(self._i2c_address, self._HIGH_THRESHOLDS + (channel << 1), struct.pack("<H", value))

    def low_threshold(self, channel, value):
        self._i2c.writeto_mem(self._i2c_address, self._LOW_THRESHOLDS + (channel << 1), struct.pack("<H", value))

    @property
    def analog_values(self):
        return struct.unpack("<HHHHH", self._i2c.readfrom_mem(self._i2c_address, self._ANALOG_VALUES, 10))

    @property
    def digital_values(self):
        resp = self._i2c.readfrom_mem(self._i2c_address, self._DIGITAL_VALUES, 1)[0]
        data = [0] * 5
        for length in range(5):
            data[length] = (resp >> length) & 0x01
        return data

    def analog_value(self, channel):
        return self.analog_values[channel]

    def digital_value(self, channel):
        return self.digital_values[channel]


class FollowerLine2:
    """ 2路红外 """
    def __init__(self, out1=2, out2=4):
        self.out1 = machine.Pin(out1, machine.Pin.OUT)
        self.out2 = machine.Pin(out2, machine.Pin.OUT)
    
    def digital_values(self):
        return [self.out1.value(), self.out2.value()]
